package top.rish.utils.common;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class DateUtils {
    public static final String yyyyMMddHHmmss = "yyyy-MM-dd HH:mm:ss";
    public static final String yyyyMMdd = "yyyy-MM-dd";
    public static final String HHmmdd = "HH:mm:ss";
    public static final String DAY = "day", MONTH = "month", YEAR = "year", WEEK = "week";
    public static final String HOUR = "hour", MINNUTE = "minute", SECOND = "second";
    /**
     * 日期正则表达式
     */
    public static final String YEAR_REGEX = "^\\d{4}$";
    public static final String MONTH_REGEX = "^\\d{4}(\\-|\\/|\\.)\\d{1,2}$";
    public static final String DATE_REGEX = "^\\d{4}(\\-|\\/|\\.)\\d{1,2}\\1\\d{1,2}$";

    /**
     * 将日期转换为指定格式的字符串
     *
     * @param date   java.util.Date 日期（如果为NULL，则默认为当前时间）
     * @param format 需要转换后得到的格式（例如："yyyy-MM-dd HH:mm:ss"）{@link DateUtils#yyyyMMddHHmmss}
     * @return
     */
    public static String getDateTimeStr(Date date, String format) {
        if (date == null) {
            date = new Date();
        }
        if (format == null || format.isEmpty()) {
            format = yyyyMMddHHmmss;
        }
        return new SimpleDateFormat(format).format(date);
    }

    /**
     * 日期字符串 → 日期（java.util.Date）
     *
     * @param dateTimeStr 日期字符串（例如："yyyy-MM-dd HH:mm:ss"）
     * @param format      转成日期的格式（注意：必须和日期字符串中的元素对应上）{@link DateUtils#yyyyMMddHHmmss}
     * @return
     */
    public static Date getDateTime(String dateTimeStr, String format) {
        if (format == null || format.isEmpty()) {
            format = yyyyMMdd;
        }
        Date date = null;
        try {
            date = new SimpleDateFormat(format).parse(dateTimeStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 获取指定日期所在月份的天数
     *
     * @param dateStr 格式最低要求为："yyyy-MM-dd"
     * @return
     */
    public static String getDaysOfMonth(String dateStr) {
        return null;
    }

    /**
     * 时间戳转化为北京时间日期格式
     *
     * @param pattern   如：yyyy-MM-dd HH:mm:ss 获取时间格式为:  年-月-日 时:分:秒<br>
     *                  pattern模式匹配语法：
     *                  G 年代标志符<br>
     *                  y 年<br>
     *                  M 月<br>
     *                  d 日<br>
     *                  h 时 在上午或下午 (1~12)<br>
     *                  H 时 在一天中 (0~23)<br>
     *                  m 分<br>
     *                  s 秒<br>
     *                  S 毫秒<br>
     *                  E 星期<br>
     *                  D 一年中的第几天<br>
     *                  F 一月中第几个星期几<br>
     *                  w 一年中第几个星期<br>
     *                  W 一月中第几个星期<br>
     *                  a 上午 / 下午 标记符<br>
     *                  k 时 在一天中 (1~24)<br>
     *                  K 时 在上午或下午 (0~11)<br>
     *                  z 时区<br>
     * @param timestamp 时间戳
     * @return
     */
    public static String timestamp2DateTime(String pattern, long timestamp) {
        //使用简体中文所在的时区
        SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.SIMPLIFIED_CHINESE);
        return format.format(timestamp);
    }

    /**
     * 获取当前时间的时间戳，精确到秒。
     *
     * @return 时间戳
     */
    public static long getTimestamp() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 时间戳 → 指定格式的字符串
     *
     * @param format    格式
     * @param timestamp 时间戳
     * @return
     */
    public static String getDateTime(String format, long timestamp) {
        return new SimpleDateFormat(format).format(timestamp);
    }

    /**
     * 判断日期是否为月底最后一天
     *
     * @param date 日期（java.util.Date）
     * @return true:是  false：否
     */
    public static boolean isLastDayofMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        Calendar last = Calendar.getInstance();
        calendar.setTime(date);
        last.set(Calendar.DAY_OF_MONTH, last.getActualMaximum(Calendar.DAY_OF_MONTH));

        // 1. 此方法时间精确到秒
        /*if (calendar.compareTo(last) == 0) {
            return true;
        }*/

        // 2. 获取当月最大天数与当前日期比较
        String start = getDateTimeStr(date, yyyyMMdd);
        String day = start.split("-")[2];
        int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); // 当月最大天数
        if (days == Integer.parseInt(day)) {
            return true;
        }
        return false;
    }

    /**
     * 计算两时间相距多少天
     *
     * @param startDate 开始时间（最低格式要求："yyyy-MM-dd"）
     * @param endDate   结束时间（最低格式要求："yyyy-MM-dd"）
     * @return 正数：开始时间>结束时间；<br/> 负数：开始时间 < 结束时间<br/> 0：①起止时间相同；②传入的起止时间为NULL或空字符
     */
    public static Integer calDateApart(String startDate, String endDate) {
        startDate = startDate == null || startDate.isEmpty() ? getDateTimeStr(null, yyyyMMdd) : startDate;
        endDate = endDate == null || endDate.isEmpty() ? getDateTimeStr(null, yyyyMMdd) : endDate;
        DateFormat df = new SimpleDateFormat(yyyyMMdd);
        Date d1 = new Date(), d2 = new Date();
        try {
            d1 = df.parse(startDate);
            d2 = df.parse(endDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        Calendar start = Calendar.getInstance();
        start.setTime(d1);
        Long startTime = start.getTimeInMillis();
        Calendar end = Calendar.getInstance();
        end.setTime(d2);
        Long endTime = end.getTimeInMillis();

        Long oneDay = 1000 * 60 * 60 * 24l, days = (endTime - startTime) / oneDay;
        int day = Integer.parseInt(String.valueOf(days));
        return day;
    }

    /**
     * 取得新的日期
     *
     * @param date 日期
     * @param days 天数
     * @return 新的日期
     */
    public static Date addDays(Date date, long days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DATE, (int) days);
        return cal.getTime();
    }

    /**
     * 比较两个时间大小(注意：两个日期中任意一个传入了NULL都会返回 0 )
     *
     * @param date1 第一个时间
     * @param date2 第二个时间
     * @return -1：date1 > date2 <br/>  1：date1 < date2; <br/> 0：date1 = date2 或 两者任一个中传入了NULL
     */
    public static int compareDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            return 0;
        }
        long a = date1.getTime(), b = date2.getTime();
        return a < b ? 1 : (a > b ? -1 : 0);
    }

    public static boolean isBefore(Date date1, Date date2) {
        long a = date1.getTime(), b = date2.getTime();
        if (a < b) {
            return true;
        }
        return false;
    }

    /**
     * 得到两个时间差  格式yyyy-MM-dd HH:mm:ss
     *
     * @param start 2019-06-27 14:12:40
     * @param end   2019-08-27 14:12:40
     * @return 5270400000
     */
    public static long dateSubtraction(String start, String end) {
        SimpleDateFormat df = new SimpleDateFormat(yyyyMMddHHmmss);
        try {
            Date date1 = df.parse(start), date2 = df.parse(end);
            return date2.getTime() - date1.getTime();
        } catch (ParseException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取当前日期是一个星期的第几天(注意：星期一为第一天，周日为最后一天)
     *
     * @param date 日期（java.util.Date）
     * @return 2
     */
    public static int getDayOfWeek(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_WEEK) - 1;
    }

    /**
     * 判断指定时间是否在[startTime, endTime]区间（注意：时间格式要一致）
     *
     * @param nowTime        指定时间
     * @param dateSection 时间区间   2020-01-08,2020-09-09
     * @return true：是，false: 否
     */
    public static boolean isEffectiveDate(Date nowTime, String dateSection) {
        try {
            String[] times = dateSection.split(",");
            Date startTime = new SimpleDateFormat(yyyyMMdd).parse(times[0]);
            Date endTime = new SimpleDateFormat(yyyyMMdd).parse(times[1]);
            if (nowTime.getTime() == startTime.getTime()
                    || nowTime.getTime() == endTime.getTime()) {
                return true;
            }
            Calendar date = Calendar.getInstance();
            date.setTime(nowTime);

            Calendar begin = Calendar.getInstance();
            begin.setTime(startTime);

            Calendar end = Calendar.getInstance();
            end.setTime(endTime);

            if (isSameDay(date, begin) || isSameDay(date, end)) {
                return true;
            }
            if (date.after(begin) && date.before(end)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        if (cal1 != null && cal2 != null) {
            return cal1.get(0) == cal2.get(0) && cal1.get(1) == cal2.get(1) && cal1.get(6) == cal2.get(6);
        } else {
            throw new IllegalArgumentException("The date must not be null");
        }
    }

    /**
     * 获取指定日期前后N个周期
     *
     * @param date   指定日期
     * @param period 周期（限定周期: 天"day"，月"month"，年"year"，周"week"）
     * @param count  N (正数：前多少个周期；<br/> 负数：后多少个周期的时间)
     * @return 2019-08-26
     */
    public static String getDatePeriod(Date date, String period, int count) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (YEAR.equals(period)) {
            calendar.add(Calendar.YEAR, count);
        } else if (MONTH.equals(period)) {
            calendar.add(Calendar.MONTH, count);
        } else if (WEEK.equals(period)) {
            calendar.add(Calendar.WEEK_OF_MONTH, count);
        } else {
            calendar.add(Calendar.DAY_OF_MONTH, count);
        }
        date = calendar.getTime();
        return new SimpleDateFormat(yyyyMMdd).format(date);
    }

    /**
     * 获取最近七天(之前的七天)
     *
     * @return 2019-08-20
     */
    public static String getServen() {
        SimpleDateFormat sdf = new SimpleDateFormat(yyyyMMdd);
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, -7);
        Date monday = c.getTime();
        String preMonday = sdf.format(monday);
        return preMonday;
    }

    /**
     * 获取最近的一个月（之前的一个月）
     *
     * @return
     */
    public static String getPreOneMonth() {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MONTH, -1);
        return new SimpleDateFormat(yyyyMMdd).format(c.getTime());
    }

    /****
     * 获取月末最后一天
     *
     * @param sDate 2014-11-24
     * @return 30或31或28或29
     */
    private static String getMonthMaxDay(String sDate) {
        Calendar cal = Calendar.getInstance();
        Date date = null;
        try {
            date = new SimpleDateFormat(yyyyMMdd).parse(sDate + "-01");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        cal.setTime(date);
        int last = cal.getActualMaximum(Calendar.DATE);
        return String.valueOf(last);
    }

    /**
     * 日期范围 - 切片
     * <pre>
     * -- eg:
     * 年 ----------------------- sliceUpDateRange("2018", "2020");
     * rs: [2018, 2019, 2020]
     *
     * 月 ----------------------- sliceUpDateRange("2018-06", "2018-08");
     * rs: [2018-06, 2018-07, 2018-08]
     *
     * 日 ----------------------- sliceUpDateRange("2018-06-30", "2018-07-02");
     * rs: [2018-06-30, 2018-07-01, 2018-07-02]
     * </pre>
     *
     * @param startDate 起始日期
     * @param endDate   结束日期
     * @return 切片日期
     */
    public static List<String> sliceUpDateRange(String startDate, String endDate) {
        List<String> rs = new ArrayList<>();
        try {
            int dt = Calendar.DATE;
            String pattern = "yyyy-MM-dd";
            if (startDate.matches(YEAR_REGEX)) {
                pattern = "yyyy";
                dt = Calendar.YEAR;
            } else if (startDate.matches(MONTH_REGEX)) {
                pattern = "yyyy-MM";
                dt = Calendar.MONTH;
            } else if (startDate.matches(DATE_REGEX)) {
                pattern = "yyyy-MM-dd";
                dt = Calendar.DATE;
            }
            Calendar sc = Calendar.getInstance(), ec = Calendar.getInstance();
            sc.setTime(getDateTime(startDate, pattern));
            ec.setTime(getDateTime(endDate, pattern));
            while (sc.compareTo(ec) < 1) {
                rs.add(getDateTimeStr(sc.getTime(), pattern));
                sc.add(dt, 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 获得本周的最后一天，周日
     *
     * @return
     */
    public static Date getCurrentWeekDayEndTime() {
        Calendar c = Calendar.getInstance();
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK) - 1;
            if (weekday == 0) {
                weekday = 7;
            }
            c.add(Calendar.DATE, -weekday + 7);
            c.setTime(new SimpleDateFormat().parse(new SimpleDateFormat().format(c.getTime()) + " 23:59:59"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }

    /**
     * 获得本周的第一天（即周一）
     *
     * @return
     */
    public static Date getCurrentWeekDayStartTime() {
        Calendar c = Calendar.getInstance();
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK) - 1;
            if (weekday == 0) {
                weekday = 7;
            }
            c.add(Calendar.DATE, -weekday + 1);
            c.setTime(new SimpleDateFormat().parse(new SimpleDateFormat().format(c.getTime()) + " 00:00:00"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }

    /**
     * 获得指定日期所在日的开始时间
     *
     * @return
     */
    public static String getCurrentDayStartTime(Date date) {
        String result = (date == null)
                ? new SimpleDateFormat(yyyyMMdd).format(System.currentTimeMillis())
                : getDateTimeStr(date, yyyyMMdd);
        return result.concat(" 00:00:00");
    }

    /**
     * 获得指定日期所在日的结束时间
     *
     * @return
     */
    public static String getCurrentDayEndTime(Date date) {
        String result = (date == null)
                ? new SimpleDateFormat(yyyyMMdd).format(System.currentTimeMillis())
                : getDateTimeStr(date, yyyyMMdd);
        return result.concat(" 23:59:59");
    }

    /**
     * 根据日期范围，得到按周期划分的日期区间值
     *
     * @param dateRange 格式最低要求有年月日，例如："2020-01-01,2020-03-31"
     * @param period    周期（日"day"，月"month"，年"year"）
     * @return 小区间集合
     */
    public static List<String> getPeriodDateList(String dateRange, String period) {
        String startDate = dateRange.trim().split(",")[0], endDate = dateRange.trim().split(",")[1];
        Date start = getDateTime(startDate, yyyyMMdd), end = getDateTime(endDate, yyyyMMdd);
        List<String> result = new ArrayList<>();
        if (DAY.equals(period)) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(start);
            while (start.before(end) || start.equals(end)) {
                result.add(new SimpleDateFormat(yyyyMMdd).format(calendar.getTimeInMillis()));
                // calendar.getTimeInMillis();
                // String startTmp = getCurrentDayStartTime(start), endTmp = getCurrentDayEndTime(start);
                // result.add(startTmp.concat(",").concat(endTmp));
                calendar.add(Calendar.DATE, 1);
                start = calendar.getTime();
            }
        } else if (MONTH.equals(period)) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(start);
            while (start.before(end) || start.equals(end)) {
                result.add(new SimpleDateFormat("yyyy-MM").format(calendar.getTimeInMillis()));
                calendar.add(Calendar.MONTH, 1);
                start = calendar.getTime();
            }
        } else if (YEAR.equals(period)) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(start);
            while (start.before(end) || start.equals(end)) {
                result.add(new SimpleDateFormat("yyyy").format(calendar.getTimeInMillis()));
                calendar.add(Calendar.YEAR, 1);
                start = calendar.getTime();
            }
        }
        return result;
    }

    /**
     * 获取指定日期前后count天的集合
     *
     * @param date  指定日期
     * @param count 多少天（正数：之后；负数：之前）
     * @return 前/后日期的集合（包含指定日期）
     */
    public static List<String> getDayList(Date date, int count) {
        List<String> result = new ArrayList<>();
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        boolean flag = false;
        if (count < 0) {
            count = Math.abs(count);
            flag = true;
        }
        for (int i = 0; i < count; i++) {
            result.add(new SimpleDateFormat(yyyyMMdd).format(c.getTimeInMillis()));
            c.add(Calendar.DATE, flag ? -1 : 1);
        }
        if (flag){
            Collections.reverse(result);
        }
        return result;
    }

    public static void main(String[] args) {
//        Date date = new Date(), dateDemo = getDateTime("2020-01-30 23:33:21", yyyyMMddHHmmss);
//        String dateStr = "2020-02-01", timeStr = "23:33:21", dateTimeStr = "2020-01-30 23:33:21";
//        String dateStr2 = "2020-01-31", timeStr2 = "23:33:21", dateTimeStr2 = "2020-01-30 23:33:21";
//        String dateRange = "2020-01-01,2020-02-05";
        System.out.println(getDayList(new Date(), -7));
//        System.out.println(getPeriodDateList(dateRange, DAY));
//        System.out.println(getCurrentDayStartTime(dateDemo));
//        System.out.println(getCurrentDayEndTime(dateDemo));
//        System.out.println(getDateTimeStr(getCurrentWeekDayStartTime(), yyyyMMdd));
//        System.out.println(getDateTimeStr(getCurrentWeekDayEndTime(), yyyyMMdd));
//        System.out.println(sliceUpDateRange(dateRange.trim().split(",")[0], dateRange.trim().split(",")[1]));
//        System.out.println(getPeriodDateList(dateRange, DAY));
//        System.out.println(getDateTimeStr(null, "hh mm ss"));
//        System.out.println(getDateTime(dateTimeStr, yyyyMMddHHmmss));
//        System.out.println(getTimestamp());
//        System.out.println(getDateTime(yyyyMMdd, System.currentTimeMillis()));
//        System.out.println(timestamp2DateTime(yyyyMMddHHmmss, System.currentTimeMillis()));
//        System.out.println(isLastDayofMonth(getDateTime(dateStr, yyyyMMdd)));
//        System.out.println(calDateApart(dateStr, dateStr));
//        System.out.println(compareDay(getDateTime(dateStr, yyyyMMdd), getDateTime(dateStr2, yyyyMMdd)));
//        System.out.println(getDateTimeStr(addDays(date, 3), yyyyMMdd));
//        System.out.println(dateSubtraction(dateTimeStr, dateTimeStr2));
//        System.out.println(getDayOfWeek(date));
//        System.out.println(isEffectiveDate(getDateTime(dateTimeStr, yyyyMMdd), dateStr.concat(",").concat(dateStr2)));
//        System.out.println(getDatePeriod(date, WEEK, -4));
//        System.out.println(getServen());
//        System.out.println(getPreOneMonth());
//        System.out.println(getMonthMaxDay(dateStr));
    }
}